#include "schedule.h"

#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sched.h>

#include "log.h"

uint sched_interval = 1000;

struct jit_sched_context {
    bool active;
};

static void* jit_sched_start(struct seq_file *seq, loff_t *pos) {
    LOGI("jitsched start pos: %lld", *pos);
    struct jit_sched_context *ctx = seq->private;
    if (!ctx) {
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx) {
            LOGE("out of memory");
            return NULL;
        }
        LOGI("alloc new context 0x%p", ctx);
        ctx->active = true;
        seq->private = ctx;
    }
    LOGI("jitsched start exit");
    return ctx;
}

static void* jit_sched_next(struct seq_file *seq, void *v, loff_t *pos) {
    LOGI("jitsched next pos: %lld", *pos);
    struct jit_sched_context *ctx = seq->private;

    if (signal_pending(current) || !ctx->active) {
        LOGI("interrupted");
        ctx->active = false;
        return NULL;
    }

    (void)(*pos)++;
    LOGI("jitsched next exit pos: %lld", *pos);
    return ctx;
}

static int jit_sched_show(struct seq_file *seq, void *v) {
    LOGI("jitsched show");
    ulong begin = jiffies;
    ulong timeout_j = begin + msecs_to_jiffies(sched_interval);

    // busy waiting
    while (time_is_after_eq_jiffies(timeout_j)) {
        schedule();
    }
    seq_printf(seq, "% 12ld% 12ld\n", begin, jiffies);
    return 0;
}

static void jit_sched_stop(struct seq_file *seq, void *v) {
    LOGI("jitsched stop");
    struct jit_sched_context *ctx = seq->private;

    if (ctx) {
        LOGI("free context 0x%p", ctx);
        kfree(ctx);
        seq->private = NULL;
    }
    LOGI("jitsched stop exit");
}

static const struct seq_operations jit_sched_ops = {
    .start = jit_sched_start,
    .next = jit_sched_next,
    .show = jit_sched_show,
    .stop = jit_sched_stop,
};

static int jit_sched_open(struct inode *inode, struct file *filp) {
    return seq_open(filp, &jit_sched_ops);
}

// For the situation having only a single record,
// we can use single_open/release impl.
const struct proc_ops sched_ops = {
    .proc_open = jit_sched_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = seq_release,
};